/*!
 * Node-animation module v1.0.0 of jRaiser javascript library
 * http://code.google.com/p/jraiser/
 *
 * Author: Heero.Luo
 * Licensed under MIT license
 *
 * Date: Tue Jun 05 2012 10:35:03 GMT+0800
 */
define(function(require, exports, module) { 'use strict';

var core = require('core/1.0.x/core'),
	$base = require('node-base/1.0.x/node-base'),
	$style = require('node-style/1.0.x/node-style');


var animationQueue = [ ],		// 动画队列
	animationTimerId;			// 执行动画的setInterval返回的timer id

// 把动作加入动画队列，并执行队列（如果未执行）
function doAnimation(action) {
	var actionId = animationQueue.push(action) - 1;

	if (!animationTimerId) {
		animationTimerId = setInterval(function() {
			for (var i = 0, len = animationQueue.length; i < len; i++) {
				animationQueue[i] && animationQueue[i]();
			}
		}, 13);
	}
	
	// 返回动作在队列中的序号
	return actionId;
}

// 把动作从动画队列中移除
function clearAnimation(id) {
	animationQueue[id] = null;
	
	// 没有动画，可以清理动画队列
	if (animationQueue.join('') === '') {
		animationQueue = [ ];
		
		if (animationTimerId) {
			clearInterval(animationTimerId);
			animationTimerId = null;
		}
	}
}


var rNumber = /^\d+(?:\.\d+)?[^\s]*$/,
	rRelNumber = /^([+-])(\d+(?:\.\d+)?)[^\s]*$/,
	rColor = /color$/i,
	rSharpColor = /^#[a-f0-9]{6}$/i,
	rRGBColor = /^rgb\((\d+),\s(\d+),\s(\d+)\)$/,
	defaultStyleValues = { opacity : 1 };


// 转换样式值，数值去掉单位
function parseStyleValue(val) {
	if (typeof val === 'string') {
		if ( rNumber.test(val) ) {
			val = parseFloat(val) || 0;
		} else if ( rSharpColor.test(val) ) {
			val = [
				parseInt(val.substr(1, 2), 16),
				parseInt(val.substr(3, 2), 16),
				parseInt(val.substr(5, 2), 16)
			];
		} else if ( rRGBColor.test(val) ) {
			val = [
				parseInt(RegExp.$1),
				parseInt(RegExp.$2),
				parseInt(RegExp.$3)
			];
		} else {
			val = val.toLowerCase();
		}
	}

	return val;
}

// 获取与最终样式相对应的初始样式值
function getCurStyles(node, finalStyles) {
	var curStyles = { };
	for (var n in finalStyles) {
		curStyles[n] = n === 'width' || n === 'height' ?
			$style.getSize(node)[n] : parseStyleValue( $style.getStyle(node, n) );
			
		if ( curStyles[n] === '' && defaultStyleValues.hasOwnProperty(n) ) {
			curStyles[n] = defaultStyleValues[n];
		}
	}
	
	return curStyles;
}


// 检查节点是否支持动画操作
function isSupportAnimation(node) {
	return node.nodeType === 1 && !$base.isXMLNode(node);
}

// 运动方程
var transitions = {
	linear : function(initVal, diff, progress, escapedTime) { return initVal + diff * progress; },
	
	swing : function(initVal, diff, progress, escapedTime) {
		return ( ( -Math.cos( progress * Math.PI ) / 2 ) + 0.5 ) * diff + initVal;
	}
};


// 计算动画过程中的下一个值
function calculateVal(cVal, fVal, transition, progress, escapedTime) {
	var nVal;

	if ( core.isArray(cVal) && core.isArray(fVal) ) {
		nVal = [ ];
		cVal.forEach(function(v, i) {
			nVal[i] = parseInt( calculateVal(v, fVal[i], transition, progress, escapedTime) );
		});
	} else if (typeof cVal === 'number') {
		// 相对数值
		if ( rRelNumber.test(fVal) ) { fVal = cVal + parseFloat(fVal); }
		
		if (typeof cVal === 'number') {
			nVal = cVal + (fVal - cVal) * transition(0, 1, progress);
			// 防止超出界限值
			if ( (fVal > cVal && nVal > fVal) || (fVal < cVal && nVal < fVal) ) {
				nVal = fVal;
			}
		}
	} else {
		nVal = fVal;
	}

	return nVal;
}


// 存放节点id以及对应的动画动作id
var animationSpace = { };

/**
 * 对指定节点执行动画
 * @param {DOMElement} 节点
 * @param {Object} 最终样式
 * @param {Number} 动画执行时间
 * @param {Function|String} 动画过渡效果
 * @param {Function} 动画结束后的回调
 * @param {Boolean} 此参数主要用于循环调用的时候，忽略对最终样式值的修正
 */
function startAnimation(node, finalStyles, duration, transition, callback, isPass) {
	if ( !isSupportAnimation(node) ) { return; }
	
	duration = duration || 500;

	transition = transition || 'linear';
	if (typeof transition === 'string') {
		if ( transitions.hasOwnProperty(transition) ) {
			transition = transitions[transition];
		} else {
			throw new Error('not such transition(' + transition + ')');
		}
	}
	
	if (!isPass) {
		// 修正最终样式的样式值
		for (var n in finalStyles) {
			finalStyles[n] = parseStyleValue(finalStyles[n]);
		}
	}
	
	// 获取节点的当前样式
	var curStyles = getCurStyles(node, finalStyles);

	// 停止已有的动画，防止冲突
	stopAnimation(node);
	
	// 先设为可见，不然看不到动画效果
	if (finalStyles.visibility === 'visible') { $style.setStyle(node, 'visibility', 'visible'); }
	if (finalStyles.display === 'block') { $style.setStyle(node, 'display', 'block'); }

	var nodeId = $base.uniqueId(node), startTime = +new Date;

	var actionId = doAnimation(function() {
		// 计算动画进度
		var escapedTime = +new Date - startTime, progress = escapedTime / duration;

		var cVal,	// 当前样式值
			fVal,	// 最终样式值
			nVal;	// 新样式值
		for (var n in finalStyles) {
			cVal = curStyles[n];
			fVal = finalStyles[n];

			if (n === 'visilibity' || n === 'display') { continue; }

			nVal = calculateVal(cVal, fVal, transition, progress, escapedTime);

			if (cVal !== fVal) {
				$style.setStyle(node, n, rColor.test(n) ? 'rgb(' + nVal.join(', ') + ')' : nVal);
			}
		}
	
		if (escapedTime >= duration) {
			stopAnimation(node);

			if (finalStyles.visibility  === 'hidden') { $style.setStyle(node, 'visibility', 'hidden'); }
			if (finalStyles.display === 'none') { $style.setStyle(node, 'display', 'none'); }
			
			// 回调
			callback && callback.call(node);
		}
	});

	// 记录动画的Id，方便清除
	animationSpace[nodeId] = actionId;
}

/**
 * 停止指定节点的动画
 * @param {DOMElement} 节点
 */
function stopAnimation(node) {
	if (!isSupportAnimation(node)) { return; }
	
	var nodeId = $base.uniqueId(node), actionId = animationSpace[nodeId];
	if (actionId != null) {
		clearAnimation(actionId);
		delete animationSpace[nodeId];
	}
}


/**
 * See line 147
 */
exports.start = startAnimation;

/**
 * See line 224
 */
exports.stop = stopAnimation;

/**
 * node模块的扩展接口
 */
exports.nodeApis = {
	/**
	 * 对当前所有节点执行动画
	 * @param {Object} 最终样式
	 * @param {Number} 动画执行时间，默认为500ms
	 * @param {Function|String} 动画过渡效果
	 * @param {Function} 动画结束后的回调
	 * @return {NodeList} 当前节点集合
	 */
	animate : function(finalStyles, duration, transition, callback) {
		for (var n in finalStyles) {
			finalStyles[n] = parseStyleValue(finalStyles[n]);
		}
		
		this.forEach(function(node) {
			startAnimation(node, finalStyles, duration, transition, callback, true);
		});
		
		return this;
	},
	
	/**
	 * 停止当前所有节点的动画
	 * @return {NodeList} 当前节点集合
	 */
	stopAnimation : function() {
		this.forEach(function(node) {
			stopAnimation(node);
		});
		
		return this;
	}
};

});